#include "RingManager.h"
#include "TileManager.h"

RingManager::RingManager( vPlanet *_planet, double inner_rad, double outer_rad ) : D3D11Effect() {
	planet = _planet;
	irad = inner_rad;
	orad = outer_rad;
	rres = (DWORD)-1;
	tres = 0;
	ntex = 0;
	memset( mesh, 0, 3*sizeof(MSH) );
	memset( tex, 0, 3*sizeof(ID3D11ShaderResourceView*) );

	for( DWORD j = 0; j < 3; j++ )
		mesh[j] = CreateRing( 8 + j*4 );
	ntex = LoadTextures();
}

RingManager::~RingManager() {
	for( DWORD j = 0; j < 3; j++ ) {
		REL( mesh[j].VB );
		REL( mesh[j].IB );
	}
	for( DWORD j = 0; j < ntex; j++ )
		REL( tex[j] );
}

void RingManager::SaveParams() {
//calls to oapi not allowed from rendering thread
	oapiGetRotationMatrix( planet->obj, &grot );
}

void RingManager::Render( D3DXMATRIX *mWorld, DWORD res, bool front ) {
	const UINT RVertexStride = 20, VBOffset = 0;
//resolution
	if( res != rres ) {
		rres = res;
		tres = min( rres, ntex-1 );
	}
//render
	float BlendFactors[4] = { 1.0f };
	D3DXVECTOR3 q( mWorld->_11, mWorld->_21, mWorld->_31 );
	float scale = D3DXVec3Length( &q );

	VECTOR3 _y = mul( grot, _V( 0.0, 1.0, 0.0 ) );
	VECTOR3 _x = unit( crossp( planet->GetCPos(), _y ) );
	VECTOR3 _z = unit( crossp( _x, _y ) );

	if( !front ) {
		_x = -_x;
		_z = -_z;
	}

	D3DXVECTOR3 x( (float)_x.x, (float)_x.y, (float)_x.z );
	D3DXVECTOR3 y( (float)_y.x, (float)_y.y, (float)_y.z );
	D3DXVECTOR3 z( (float)_z.x, (float)_z.y, (float)_z.z );
	x *= scale;	y *= scale;	z *= scale;

	D3DXMATRIX M[2];
	
	M[0] = *mWorld;
	D3DMAT_FromAxisT( &M[0], &x, &y, &z );
	M[1] = *SC->GetVP();

	dCtx->UpdateSubresource( cb_VS_Ring, 0, NULL, M, 0, 0 );
	dCtx->VSSetConstantBuffers( 0, 1, &cb_VS_Ring );

	D3DXVECTOR4 NormW;
	D3DXVec4Transform( &NormW, &D3DXVECTOR4( 0.0f, 1.0f, 0.0f, 0.0f ), &M[0] );
	D3DXVec4Normalize( &NormW, &NormW );

	dCtx->UpdateSubresource( cb_PS_Ring, 0, NULL, &NormW, 0, 0 );
	dCtx->PSSetConstantBuffers( 0, 1, &cb_PS_Ring );

	dCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	dCtx->IASetInputLayout( IL_RVertex );
	dCtx->VSSetShader( VS_Ring, NULL, NULL );
	dCtx->PSSetShader( PS_Ring, NULL, NULL );

	dCtx->IASetVertexBuffers( 0, 1, &mesh[rres].VB, &RVertexStride, &VBOffset );
	dCtx->IASetIndexBuffer( mesh[rres].IB, DXGI_FORMAT_R16_UINT, 0 );

	dCtx->PSSetShaderResources( 0, 1, &tex[rres] );
	dCtx->PSSetSamplers( 0, 1, &SS_Ring );

	dCtx->OMSetBlendState( BS_SrcAlpha_Blend_0, BlendFactors, 0xFFFFFFFF );
	dCtx->OMSetDepthStencilState( DSS_Write0_Test0, 0 );
	dCtx->RSSetState( RS_None_Solid );

	dCtx->DrawIndexed( mesh[rres].nidx, 0, 0 );
}

MSH RingManager::CreateRing( DWORD nsect ) {
	MSH mesh;
	DWORD i, j;

	int count = nsect/2 + 1;
	int nvtx = 2*count;
	mesh.nidx = 6*(count-1);

	RVERTEX *Vtx = new RVERTEX [nvtx+4];
	WORD *Idx = new WORD [mesh.nidx+12];

	double alpha = PI/(double)nsect;
	float nrad = (float)(orad/cos( alpha )); // distance for outer nodes
	float ir = (float)irad;
	float fo = (float)(0.5*(1.0 - orad/nrad));
	float fi = (float)(0.5*(1.0 - irad/nrad));

	for( i = j = 0; i < count; i++ ) {
		double phi = i*2.0*alpha;
		float cosp = (float)cos( phi ),	sinp = (float)sin( phi );

		Vtx[i*2].pos.x = nrad*cosp;
		Vtx[i*2].pos.z = nrad*sinp;
		Vtx[i*2].pos.y = 0.0f;

		Vtx[i*2+1].pos.x = ir*cosp;
		Vtx[i*2+1].pos.z = ir*sinp;
		Vtx[i*2+1].pos.y = 0.0f;

		if( !(i & 1) )	Vtx[i*2].tex.x = fo,		Vtx[i*2+1].tex.x = fi;
		else			Vtx[i*2].tex.x = 1.0f - fo,	Vtx[i*2+1].tex.x = 1.0f - fi;

		Vtx[i*2].tex.y = 0.0f,	Vtx[i*2+1].tex.y = 1.0f;

		if( (DWORD)j <= (mesh.nidx-6) ) {
			Idx[j++] = i*2;
			Idx[j++] = i*2+1;
			Idx[j++] = i*2+2;
			Idx[j++] = i*2+3;
			Idx[j++] = i*2+2;
			Idx[j++] = i*2+1;
		}
	}

	D3D11_BUFFER_DESC desc;
	ZeroMemory( &desc, sizeof(desc) );
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc.ByteWidth = nvtx*20;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
	desc.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sdata;
	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( Dev->CreateBuffer( &desc, &sdata, &mesh.VB ) );

	ZeroMemory( &desc, sizeof(desc) );
	desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	desc.ByteWidth = mesh.nidx*2;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	desc.StructureByteStride = 0;
	desc.Usage = D3D11_USAGE_IMMUTABLE;

	ZeroMemory( &sdata, sizeof(sdata) );
	sdata.pSysMem = Idx;

	HR( Dev->CreateBuffer( &desc, &sdata, &mesh.IB ) );

	delete [ ] Vtx;
	delete [ ] Idx;

	return mesh;
}

DWORD RingManager::LoadTextures() {
	char fname[256], cpath[256];
	oapiGetObjectName( planet->obj, fname, 256 );
	strcat_s( fname, 256, "_ring.tex" );
	if (!gc->TexturePath( fname, cpath ))
		return 0;
	return TileManager::tbuf->LoadTextures( cpath, 3, 0, tex );
}